using System;
using FireStarter.Base;
using FireStarter.Base.BusinessObjectMerger;
using Firestarter.DB.Schema.BO;
using Firestarter.DB.Schema.Logic;
using Firestarter.DB.Schema.Test.BO;
using Firestarter.DB.Schema.Test.BO.TestUtils;
using Habanero.BO;
using Habanero.BO.ClassDefinition;
using Habanero.BO.Loaders;
using Habanero.Testability;
using NUnit.Framework;

namespace Firestarter.DB.Schema.Test.Logic
{
    [TestFixture]
    public class TestMergeDBKeyColumn
    {
        [TestFixtureSetUp]
        public virtual void TestFixtureSetup()
        {
            ClassDef.ClassDefs.Clear();
            string xmlClassDefs = BOBroker.GetClassDefsXml();
            ClassDef.ClassDefs.Add(new XmlClassDefsLoader(xmlClassDefs, new DtdLoader()).LoadClassDefs());
            BORegistry.DataAccessor = new DataAccessorInMemory();

            MergeableBORegistry.Instance.Register<DBKeyColumn, DBMergeDBKeyColumn>();
        }

        [SetUp]
        public virtual void SetupTest()
        {
        }

        [TestCase("KeyID", false)]
        [TestCase("ColumnID", false)]
        [TestCase("Approved", false)]
        [TestCase("PendingChangeType", false)]
        public void Test_MustMergeProp_ShouldBeSetForProp(string propName, bool expectedMustMergeProp)
        {
            //---------------Set up test pack-------------------
            DBMergeBO<DBKeyColumn> merger = (DBMergeBO<DBKeyColumn>) MergeableBORegistry.Instance.Resolve<DBKeyColumn>();
            var dbForeignKey = new DBKeyColumn();
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            var boProp = dbForeignKey.Props[propName];
            var returnedMustMergeProp = merger.MustMergeProperty(boProp);
            //---------------Test Result -----------------------
            Assert.AreEqual(expectedMustMergeProp, returnedMustMergeProp,
                            "Prop '" + propName + "' should be MustMerge = '" + expectedMustMergeProp + "'");
        }

        [TestCase("Column", false)]
        [TestCase("Key", false)]
        public void Test_MustMergeRelationship_ShouleBeSetForRelationships(string relationshipName,
                                                                           bool expectedMustMergeRel)
        {
            //---------------Set up test pack-------------------
            DBMergeBO<DBKeyColumn> merger = (DBMergeBO<DBKeyColumn>) MergeableBORegistry.Instance.Resolve<DBKeyColumn>();
            var dbForeignKey = new DBKeyColumn();
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            var relationship = dbForeignKey.Relationships[relationshipName];
            var returnedMustMergeProp = merger.MustMergeRelationship(relationship);
            //---------------Test Result -----------------------
            Assert.AreEqual(expectedMustMergeRel, returnedMustMergeProp,
                            "Relationship '" + relationship.RelationshipName + "' should be MustMerge = '" +
                            expectedMustMergeRel + "'");
        }

        [Test]
        public void Test_MatchKeyColumn_WhenPropNameSame_AndKeyNameNotSame_ShouldMatch()
        {
            //---------------Set up test pack-------------------
            DBMergeBO<DBKeyColumn> keyColumnMerger =
                (DBMergeBO<DBKeyColumn>)MergeableBORegistry.Instance.Resolve<DBKeyColumn>();
            var fromKeyColumn = CreateKeyColumnWithKey(RandomValueGen.GetRandomString());

            var toKeyColumn = CreateKeyColumnWithKey(fromKeyColumn.ColumnName);
            //---------------Assert Precondition----------------
            Assert.IsNotNull(toKeyColumn.Column);
            Assert.IsNotNull(fromKeyColumn.Column);
            Assert.AreEqual(toKeyColumn.ColumnName, fromKeyColumn.ColumnName);
            Assert.AreNotEqual(toKeyColumn.Key.KeyName, fromKeyColumn.Key.KeyName);
            Assert.AreNotEqual(toKeyColumn.ToString(), fromKeyColumn.ToString());
            //---------------Execute Test ----------------------
            //keyColumnMerger.BusinessObject = fromKeyColumn;
            Func<DBKeyColumn, bool> matchFunction = keyColumnMerger.MatchBusinessObject(fromKeyColumn);
            var matchBusinessObject = matchFunction(toKeyColumn);
            //---------------Test Result -----------------------
            Assert.IsTrue(matchBusinessObject, "Key Columns should match");
        }

        [Test]
        public void Test_MergeKeyColumn_WhenColumnNull_ShouldDoNothing()
        {
            //---------------Set up test pack-------------------
            DBMergeBO<DBKeyColumn> keyColumnMerger =
                (DBMergeBO<DBKeyColumn>)MergeableBORegistry.Instance.Resolve<DBKeyColumn>();

            var fromKeyColumn = CreateKeyColumnWithKey(null);
            fromKeyColumn.Column = null;
            var toKeyColumn = CreateKeyColumnWithKey(null);
            toKeyColumn.Column = null;
            //---------------Assert Precondition----------------
            Assert.IsNull(fromKeyColumn.Column);
            Assert.IsNotNull(fromKeyColumn.Key);
            var fromTable = fromKeyColumn.Key.Table;
            Assert.IsNotNull(fromTable);

            Assert.IsNull(toKeyColumn.Column);
            Assert.IsNotNull(toKeyColumn.Key);
            var toTable = toKeyColumn.Key.Table;
            Assert.IsNotNull(toTable);
            //---------------Execute Test ----------------------
            keyColumnMerger.MergeBOFrom(fromKeyColumn, toKeyColumn);
            //---------------Test Result -----------------------
            Assert.Pass("If it got here all is OK");
        }

        [Test]
        public void Test_MergeKeyColumn_WhenColNameSame_WhenToTableHasColumn_ShouldSetColumnRelationshipOnKeyColumn()
        {
            //---------------Set up test pack-------------------
            DBMergeBO<DBKeyColumn> keyColumnMerger =
                (DBMergeBO<DBKeyColumn>)MergeableBORegistry.Instance.Resolve<DBKeyColumn>();

            var fromKeyColumn = CreateKeyColumnWithKey(RandomValueGen.GetRandomString());

            var toKeyColumn = CreateKeyColumnWithKey(fromKeyColumn.ColumnName);
            toKeyColumn.Column = null;
            //---------------Assert Precondition----------------
            Assert.IsNotNull(fromKeyColumn.Column);
            Assert.IsNotNull(fromKeyColumn.Key);
            var fromTable = fromKeyColumn.Key.Table;
            Assert.IsNotNull(fromTable);
            Assert.IsTrue(fromTable.Columns.Contains(fromKeyColumn.Column));

            Assert.IsNull(toKeyColumn.Column);
            Assert.IsNotNull(toKeyColumn.Key);
            var toTable = toKeyColumn.Key.Table;
            Assert.IsNotNull(toTable);
            var expectedToColumn = toTable.Columns.Find(column => column.ColumnName == fromKeyColumn.Column.ColumnName);
            Assert.IsNotNull(expectedToColumn);
            //---------------Execute Test ----------------------
            keyColumnMerger.MergeBOFrom(fromKeyColumn, toKeyColumn);
            //---------------Test Result -----------------------
            Assert.AreSame(expectedToColumn, toKeyColumn.Column);
        }

        private static DBKeyColumn CreateKeyColumnWithKey(string parentColumnName)
        {
            var key = TestUtilsDBKey.GetTestFactory()
                .WithValueFor(dbKey => dbKey.KeyName)
                .CreateValidBusinessObject();
            DBColumn dbColumn = null;
            if (!string.IsNullOrEmpty(parentColumnName))
            {
                dbColumn = TestUtilsDBColumn.GetTestFactory()
                    .WithValue(column => column.ColumnName, parentColumnName)
                    .WithValue(column => column.Table, key.Table)
                    .CreateValidBusinessObject();
            }
            var pkColumn = TestUtilsDBKeyColumn.GetTestFactory()
                .WithValue(property => property.Key, key)
                .WithValue(property => property.Column, dbColumn)
                .CreateValidBusinessObject();
            return pkColumn;
        }
    }
}